package edg2;

import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * Cria uma tabela hash vazia de tamanho tam, e salva no arquivo nomeArquivoHash
 * Compartimento que não tem nó deve ter valor igual a -1
 * @param nomeArquivoHash nome do arquivo hash a ser criado
 * @param tam tamanho da tabela hash a ser criada
 */
public class EncadeamentoExterior {

    RandomAccessFile tabHash = null, dados = null;
    Empregado emp;
    Dependente dep;
    String resultadoBusca;
    int numeroDeRegistros;
    int end = -1;

    public int h(int x) {
        return x % 7;
    }

    public void criaHash(String nomeArquivoHash, int tam) throws IOException {
        //TODO: criar a tabela hash
        try {

            tabHash = new RandomAccessFile(nomeArquivoHash, "rw");
            for (int i = 0; i < tam; i++) {
                tabHash.writeInt(-1);
            }

        } catch (IOException e) {
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
        }
    }

    public int numeroRegistrosEmpregado(String nomeArquivo) throws IOException {
        int tamanho = 0;

        try {
            tabHash = new RandomAccessFile(nomeArquivo, "r");
            tabHash.seek(8 * 4);
            tamanho = tabHash.readInt();
        } catch (IOException e) {
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
        }
        return tamanho;
    }

    public int numeroRegistrosDependentes(String nomeArquivo) throws IOException {
        int tamanho = 0;

        try {
            tabHash = new RandomAccessFile(nomeArquivo, "r");
            tabHash.seek(8 * 4);
            tamanho = tabHash.readInt();
        } catch (IOException e) {
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
        }
        return tamanho;
    }

    public void mostraTotalRegistros(String nomeArquivoEmpregados, String nomeArquivoDependentes) throws IOException {
        try {
            tabHash = new RandomAccessFile(nomeArquivoEmpregados, "rw");
            tabHash.seek(8 * 4);
            System.out.println("total de resgistros empregados: " + tabHash.readInt());
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
        }
        try {
            tabHash = new RandomAccessFile(nomeArquivoDependentes, "rw");
            tabHash.seek(8 * 4);
            System.out.println("total de resgistros Dependentes: " + tabHash.readInt());
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
        }

    }

    public void atualizaRegistrosEmpregadosEDependentes(String nomeArquivoEmpregados, String nomeArquivoDependentes, int tamanhoEmpregados, int tamanhoDependentes) throws IOException {
        try {
            tabHash = new RandomAccessFile(nomeArquivoEmpregados, "rw");
            tabHash.seek(8 * 4);
            tabHash.writeInt(tamanhoEmpregados);
        } catch (IOException e) {
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
        }
        try {
            tabHash = new RandomAccessFile(nomeArquivoDependentes, "rw");
            tabHash.seek(8 * 4);
            tabHash.writeInt(tamanhoDependentes);
        } catch (IOException e) {
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
        }

    }

    /**
     * Executa busca em Arquivos por Encadeamento Exterior (Hash)
     * Assumir que ponteiro para próximo nó é igual a -1 quando não houver próximo nó
     * @param codCli: chave do cliente que está sendo buscado
     * @param nomeArquivoHash nome do arquivo que contém a tabela Hash
     * @param nomeArquivoDados nome do arquivo onde os dados estão armazenados
     * @return o endereco onde o cliente foi encontrado, ou -1 se não for encontrado
     */
    public int buscaEmpregado(int codEmp, String nomeArquivoHash, String nomeArquivoDados) throws Exception {
        try {
            tabHash = new RandomAccessFile(nomeArquivoHash, "rw");
            dados = new RandomAccessFile(nomeArquivoDados, "rw");
            emp = new Empregado();
            end = h(codEmp);
            tabHash.seek(end * TabHash.tamanhoRegistro);
            end = tabHash.readInt();

            while (end != -1) {
                dados.seek(end * Empregado.tamanhoRegistro);
                emp = Empregado.le(dados);
                if (emp.codEmpregado == codEmp && emp.flag == Empregado.OCUPADO) {
                    break;
                } else if (emp.prox != -1) {
                    end = emp.prox;
                } else {
                    end = -1;
                }
            }
        } catch (IOException e) {
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
            if (dados != null) {
                dados.close();
            }
            System.out.println(end);
            return end;
        }
    }

    public void buscaEmpregado(String nome, String salario, String idade, String nomeArquivoHash, String nomeArquivoDados) throws IOException {
        resultadoBusca = "";
        int contador;
        contador = -1;

        try {
            dados = new RandomAccessFile(nomeArquivoDados, "rw");

            do {
                contador++;
                dados.seek(contador * Empregado.tamanhoRegistro);
                emp = Empregado.le(dados);
                if (!emp.flag) {
                    if (nome.compareTo("") != 0) {
                        if (salario.compareTo("") != 0) {
                            if (idade.compareTo("") != 0) {
                                if ((emp.nome.contains(nome)) && (emp.idade == Integer.parseInt(idade)) && (emp.salario == Float.parseFloat(salario))) {
                                    resultadoBusca += "Código: " + emp.codEmpregado + " Nome: " + emp.nome + " Salario: " + emp.salario + " Idade: " + emp.idade + "\n";
                                }
                            } else {
                                if ((emp.nome.contains(nome)) && emp.salario == Float.parseFloat(salario)) {
                                    resultadoBusca += "Código: " + emp.codEmpregado + " Nome: " + emp.nome + " Salario: " + emp.salario + " Idade: " + emp.idade + "\n";
                                }
                            }
                        } else {
                            if (emp.nome.contains(nome)) {
                                resultadoBusca += "Código: " + emp.codEmpregado + " Nome: " + emp.nome + " Salario: " + emp.salario + " Idade: " + emp.idade + "\n";
                            }
                        }
                    } else if ((nome.compareTo("") != 0) && (idade.compareTo("") != 0)) {
                        if ((emp.nome.contains(nome)) && (emp.idade == Integer.parseInt(idade))) {
                            resultadoBusca += "Código: " + emp.codEmpregado + " Nome: " + emp.nome + " Salario: " + emp.salario + " Idade: " + emp.idade + "\n";
                        }

                    } else if ((salario.compareTo("") != 0) && (idade.compareTo("") != 0)) {
                        if ((emp.salario == Float.parseFloat(salario)) && (emp.idade == Integer.parseInt(idade))) {
                            resultadoBusca += "Código: " + emp.codEmpregado + " Nome: " + emp.nome + " Salario: " + emp.salario + " Idade: " + emp.idade + "\n";
                        }
                    } else if (nome.compareTo("") != 0 && salario.compareTo("") == 0 && idade.compareTo("") == 0) {
                        if (emp.nome.contains(nome)) {
                            resultadoBusca += "Código: " + emp.codEmpregado + " Nome: " + emp.nome + " Salario: " + emp.salario + " Idade: " + emp.idade + "\n";
                        }
                    } else if (nome.compareTo("") == 0 && salario.compareTo("") != 0 && idade.compareTo("") == 0) {
                        if (emp.salario == Float.parseFloat(salario)) {
                            resultadoBusca += "Código: " + emp.codEmpregado + " Nome: " + emp.nome + " Salario: " + emp.salario + " Idade: " + emp.idade + "\n";
                        }
                    } else if (nome.compareTo("") == 0 && salario.compareTo("") == 0 && idade.compareTo("") != 0) {
                        if (emp.idade == Integer.parseInt(idade)) {
                            resultadoBusca += "Código: " + emp.codEmpregado + " Nome: " + emp.nome + " Salario: " + emp.salario + " Idade: " + emp.idade + "\n";
                        }
                    }
                }
            } while (true);

        } catch (IOException e) {
        } finally {
            if (dados != null) {
                dados.close();
            }
        }

    }

    public void buscaDependente(String nome, String codEmp, String idade, String nomeArquivoHash, String nomeArquivoDados) throws IOException {
        resultadoBusca = "";
        int contador;
        contador = -1;
        try {
            dados = new RandomAccessFile(nomeArquivoDados, "rw");
            do {
                contador++;
                dados.seek(contador * Dependente.tamanhoRegistro);
                dep = Dependente.le(dados);
                if (!dep.flag) {
                    if (nome.compareTo("") != 0) {
                        if (codEmp.compareTo("") != 0) {
                            if (idade.compareTo("") != 0) {
                                if ((dep.nome.contains(nome)) && (emp.idade == Integer.parseInt(idade)) && (dep.codEmpregado == Integer.parseInt(codEmp))) {
                                    resultadoBusca += "Código: " + dep.codDependente + " Nome: " + dep.nome + " Idade: " + dep.idade + " Código Do Empregado: " + dep.codEmpregado + "\n";
                                }
                            } else {
                                if ((dep.nome.contains(nome)) && (dep.codEmpregado == Integer.parseInt(codEmp))) {
                                    resultadoBusca += "Código: " + dep.codDependente + " Nome: " + dep.nome + " Idade: " + dep.idade + " Código Do Empregado: " + dep.codEmpregado + "\n";
                                }
                            }
                        } else {
                            if (dep.nome.contains(nome)) {
                                resultadoBusca += "Código: " + dep.codDependente + " Nome: " + dep.nome + " Idade: " + dep.idade + " Código Do Empregado: " + dep.codEmpregado + "\n";
                            }
                        }
                    } else if ((nome.compareTo("") != 0) && (idade.compareTo("") != 0)) {
                        if ((dep.nome.contains(nome)) && (dep.idade == Integer.parseInt(idade))) {
                            resultadoBusca += "Código: " + dep.codDependente + " Nome: " + dep.nome + " Idade: " + dep.idade + " Código Do Empregado: " + dep.codEmpregado + "\n";
                        }

                    } else if ((codEmp.compareTo("") != 0) && (idade.compareTo("") != 0)) {
                        if ((dep.codEmpregado == Integer.parseInt(codEmp)) && (dep.idade == Integer.parseInt(idade))) {
                            resultadoBusca += "Código: " + dep.codDependente + " Nome: " + dep.nome + " Idade: " + dep.idade + " Código Do Empregado: " + dep.codEmpregado + "\n";
                        }
                    } else if (nome.compareTo("") != 0 && codEmp.compareTo("") == 0 && idade.compareTo("") == 0) {
                        if (dep.nome.contains(nome)) {
                            resultadoBusca += "Código: " + dep.codDependente + " Nome: " + dep.nome + " Idade: " + dep.idade + " Código Do Empregado: " + dep.codEmpregado + "\n";
                        }
                    } else if (nome.compareTo("") == 0 && codEmp.compareTo("") != 0 && idade.compareTo("") == 0) {
                        if ((dep.codEmpregado == Integer.parseInt(codEmp))) {
                            resultadoBusca += "Código: " + dep.codDependente + " Nome: " + dep.nome + " Idade: " + dep.idade + " Código Do Empregado: " + dep.codEmpregado + "\n";
                        }
                    } else if (nome.compareTo("") == 0 && codEmp.compareTo("") == 0 && idade.compareTo("") != 0) {
                        if (dep.idade == Integer.parseInt(idade)) {
                            resultadoBusca += "Código: " + dep.codDependente + " Nome: " + dep.nome + " Idade: " + dep.idade + " Código Do Empregado: " + dep.codEmpregado + "\n";
                        }
                    }
                }
            } while (true);

        } catch (IOException e) {
        } finally {

            if (dados != null) {
                dados.close();
            }
        }

    }

    public int buscaDependente(int codDep, String nomeArquivoHash, String nomeArquivoDados) throws Exception {
        try {
            tabHash = new RandomAccessFile(nomeArquivoHash, "rw");
            dados = new RandomAccessFile(nomeArquivoDados, "rw");
            end = h(codDep);
            dep = new Dependente();
            tabHash.seek(end * TabHash.tamanhoRegistro);
            end = tabHash.readInt();

            while (end != -1) {
                dados.seek(end * Dependente.tamanhoRegistro);
                dep = Dependente.le(dados);
                if (dep.codDependente == codDep && dep.flag == Dependente.OCUPADO) {
                    break;
                } else if (dep.prox != -1) {
                    end = dep.prox;
                } else {
                    end = -1;
                }
            }
        } catch (IOException e) {
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
            if (dados != null) {
                dados.close();
            }
            System.out.println(end);
            return end;
        }
    }

    /**
     * Executa inserção em Arquivos por Encadeamento Exterior (Hash)
     * @param codCli: código do cliente a ser inserido
     * @param nomeCli: nome do Cliente a ser inserido
     * @param nomeArquivoHash nome do arquivo que contém a tabela Hash
     * @param nomeArquivoDados nome do arquivo onde os dados estão armazenados
     *@param numRegistros numero de registros que já existem gravados no arquivo
     * @return endereço onde o cliente foi inserido, -1 se não conseguiu inserir
     */
    public int alteraEmpregado(int codEmpregado, String novoNome, String novaIdade, String novoSalario,
            String nomeArquivoHash, String nomeArquivoDados) throws Exception {
        end = buscaEmpregado(codEmpregado, nomeArquivoHash, nomeArquivoDados);
        //System.out.println("srfkjasji"+end);
        if (end != -1) {
            dados = new RandomAccessFile(nomeArquivoDados, "rw");
            dados.seek(end * Empregado.tamanhoRegistro);
            emp = Empregado.le(dados);
            dados.seek(end * Empregado.tamanhoRegistro);
            if (novoNome.compareTo("") == 0) {
                novoNome = emp.nome;
            }
            if (novoSalario.compareTo("") == 0) {
                novoSalario = "" + emp.salario;
            }
            if (novaIdade.compareTo("") == 0) {
                novaIdade = "" + emp.idade;
            }
            Empregado novo = new Empregado(codEmpregado, novoNome, Integer.parseInt(novaIdade), Double.parseDouble(novoSalario), emp.prox, emp.numeroDependentes, Empregado.OCUPADO);
            novo.salva(dados);

        }

        dados.close();
        return end;
    }

    public int alteraDependente(int codDependente, String novoNome, String novaIdade, String novoCodEmp,
            String nomeArquivoHash, String nomeArquivoDados) throws Exception {
        end = buscaEmpregado(codDependente, nomeArquivoHash, nomeArquivoDados);
        //System.out.println("srfkjasji"+end);
        if (end != -1) {
            dados = new RandomAccessFile(nomeArquivoDados, "rw");
            dados.seek(end * Dependente.tamanhoRegistro);
            dep = Dependente.le(dados);
            dados.seek(end * Dependente.tamanhoRegistro);
            if (novoNome.compareTo("") == 0) {
                novoNome = dep.nome;
            }
            if (novoCodEmp.compareTo("") == 0) {
                novoCodEmp = "" + dep.codEmpregado;
            }
            if (novaIdade.compareTo("") == 0) {
                novaIdade = "" + dep.idade;
            }
            Dependente novo = new Dependente(codDependente, novoNome, Integer.parseInt(novaIdade), Integer.parseInt(novoCodEmp), dep.prox, Dependente.OCUPADO);
            novo.salva(dados);

        }
        dados.close();
        return end;
    }

    public void insereEmpregado(String nomeEmp, int idadeEmp, double salarioEmp, String nomeArquivoHash,
            String nomeArquivoDados, int numRegistros) throws Exception {
        int codEmp;

        try {
            do {
                codEmp = (int) (Math.random() * 1000);
            } while (buscaEmpregado(codEmp, nomeArquivoHash, nomeArquivoDados) != -1);

            Empregado aux;
            int ant;
            tabHash = new RandomAccessFile(nomeArquivoHash, "rw");
            dados = new RandomAccessFile(nomeArquivoDados, "rw");
            end = h(codEmp);
            tabHash.seek(end * TabHash.tamanhoRegistro);
            end = tabHash.readInt();
            emp = new Empregado(codEmp, nomeEmp, idadeEmp, salarioEmp, -1, 0, Empregado.OCUPADO);
            if (end != -1) {
                do {
                    dados.seek(end * Empregado.tamanhoRegistro);
                    aux = Empregado.le(dados);
                    ant = end;
                    end = aux.prox;
                    // System.out.println(end);
                } while (aux.flag == Empregado.OCUPADO && end != -1);

                //salva o cliente anterior agora com o atributo prox atualizado
                if (aux.flag == Empregado.OCUPADO) {
                    end = numRegistros;
                    dados.seek(ant * Empregado.tamanhoRegistro);
                    aux = Empregado.le(dados);
                    Empregado anterior = new Empregado(aux.codEmpregado, aux.nome, aux.idade, aux.salario, end, aux.numeroDependentes, aux.flag);
                    dados.seek(ant * Empregado.tamanhoRegistro);
                    anterior.salva(dados);

                } else {
                    end = ant;
                    dados.seek(ant * Empregado.tamanhoRegistro);
                    emp.prox = Empregado.le(dados).prox;
                }

            } else {
                end = numRegistros;
                tabHash.seek(h(codEmp) * TabHash.tamanhoRegistro);
                tabHash.writeInt(end);
            }
            // salva o cliente atual agora com o anteriro ok
            dados.seek(end * Empregado.tamanhoRegistro);
            emp.salva(dados);

        } catch (IOException e) {
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
            if (dados != null) {
                dados.close();
            }

        }
    }

    public int retornaChaveEmpregado(String nome) throws IOException {
        int contador;
        int chave = -1;
        contador = -1;
        try {
            dados = new RandomAccessFile("dadosEmpregados", "rw");
            do {
                contador++;
                dados.seek(contador * Empregado.tamanhoRegistro);
                emp = Empregado.le(dados);
                if (emp.flag == Empregado.OCUPADO) {
                    System.out.println("testando");
                    if (nome.compareTo("") != 0) {
                        if (emp.nome.contains(nome)) {
                            chave = emp.codEmpregado;
                            dados.seek(contador * Empregado.tamanhoRegistro);
                            emp.incrementaDependentes();
                            System.out.println("numero" + chave);
                            emp.salva(dados);
                            break;
                        }
                    }
                }
            } while (true);

        } catch (IOException e) {
        } finally {
            if (dados != null) {
                dados.close();
            }
        }
        System.out.println("chave retorno" + chave);
        return chave;
    }

    public int insereDependente(String nomeDep, int idadeDep, String nomeEmpregado, String nomeArquivoHash, String nomeArquivoDados, int numRegistros) throws Exception {
        try {
            Dependente aux;
            int ant, codDep, codEmp;
            codEmp = retornaChaveEmpregado(nomeEmpregado);

            if (codEmp != -1) {

                do {
                    codDep = (int) (Math.random() * 1000);
                } while (buscaDependente(codDep, nomeArquivoHash, nomeArquivoDados) != -1);

                tabHash = new RandomAccessFile(nomeArquivoHash, "rw");
                dados = new RandomAccessFile(nomeArquivoDados, "rw");
                end = h(codDep);
                tabHash.seek(end * TabHash.tamanhoRegistro);
                end = tabHash.readInt();
                dep = new Dependente(codDep, nomeDep, idadeDep, codEmp, -1, Dependente.OCUPADO);
                if (end != -1) {
                    do {
                        dados.seek(end * Dependente.tamanhoRegistro);
                        aux = Dependente.le(dados);
                        ant = end;
                        end = aux.prox;
                    } while (aux.flag == Dependente.OCUPADO && end != -1);

                    //salva o cliente anterior agora com o atributo prox atualizado
                    if (aux.flag == Dependente.OCUPADO) {
                        end = numRegistros;
                        dados.seek(ant * Dependente.tamanhoRegistro);
                        aux = Dependente.le(dados);
                        Dependente anterior = new Dependente(aux.codDependente, aux.nome, aux.idade, aux.codEmpregado, end, aux.flag);
                        dados.seek(ant * Dependente.tamanhoRegistro);
                        anterior.salva(dados);

                    } else {
                        end = ant;
                        dados.seek(end * Dependente.tamanhoRegistro);
                        dep.prox = Dependente.le(dados).prox;
                    }

                    dados.seek(end * Dependente.tamanhoRegistro);
                    dep.salva(dados);

                } else {
                    end = numRegistros;
                    tabHash.seek(h(codDep) * TabHash.tamanhoRegistro);
                    tabHash.writeInt(end);
                    dados.seek(end * Dependente.tamanhoRegistro);
                    dep.salva(dados);
                }

            }
        } catch (IOException e) {
        } finally {
            if (tabHash != null) {
                tabHash.close();
            }
            if (dados != null) {
                dados.close();
            }
        }
        return end;
    }

    /**
     * Executa exclusão em Arquivos por Encadeamento Exterior (Hash)
     * @param codCli: chave do cliente a ser excluído
     * @param nomeArquivoHash nome do arquivo que contém a tabela Hash
     * @param nomeArquivoDados nome do arquivo onde os dados estão armazenados
     * @return endereço do cliente que foi excluído, -1 se cliente não existe
     */
    public int excluiEmpregado(int CodEmp, String nomeArquivoHash, String nomeArquivoDados) throws Exception {
        end = buscaEmpregado(CodEmp, nomeArquivoHash, nomeArquivoDados);
        if (end != -1) {
            dados = new RandomAccessFile(nomeArquivoDados, "rw");
            dados.seek(end * Empregado.tamanhoRegistro);
            Empregado aux = Empregado.le(dados);
            dados.seek(end * Empregado.tamanhoRegistro);
            emp = new Empregado(aux.codEmpregado, aux.nome, aux.idade, aux.codEmpregado, aux.prox, aux.numeroDependentes, Dependente.LIBERADO);
            emp.salva(dados);
        }
        return end;
    }

    public int excluiDependente(int CodDep, String nomeArquivoHash, String nomeArquivoHashEmpregado, String nomeArquivoDados, String nomeArquivoDadosEmpregados) throws Exception {
        end = buscaDependente(CodDep, nomeArquivoHash, nomeArquivoDados);
        if (end != -1) {
            dados = new RandomAccessFile(nomeArquivoDados, "rw");
            dados.seek(end * Dependente.tamanhoRegistro);
            Dependente aux = Dependente.le(dados);
            dados.seek(end * Dependente.tamanhoRegistro);
            dep = new Dependente(aux.codDependente, aux.nome, aux.idade, aux.codEmpregado, aux.prox, Dependente.LIBERADO);
            dep.salva(dados);

            end = buscaEmpregado(dep.codEmpregado, nomeArquivoHashEmpregado, nomeArquivoDadosEmpregados);

            if (end != -1) {
                dados = new RandomAccessFile(nomeArquivoDadosEmpregados, "rw");
                dados.seek(end * Empregado.tamanhoRegistro);
                emp = Empregado.le(dados);
                dados.seek(end * Empregado.tamanhoRegistro);
                emp.decrementaDependente();
                emp.salva(dados);
            }
        }
        return end;
    }
}
